package dmxGroupControl.solvers;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import org.jdom2.Attribute;

import dmxGroupControl.Channel;
import dmxGroupControl.Fixture;
import dmxGroupControl.Group;

public class SimpleChaseSolver extends Solver
{
    private static List<String> solvableChannels = Arrays.asList("Red", "Green", "Blue", "White", "MonoMaster");
    private static List<String> solvableAttributes = Arrays.asList("Chase");
    
    Group group;
    float value = 0.0f;
    boolean wrapping = false;
    int maxDistance;
    
    ArrayList<Fixture> applicableFixtures;
    
    public SimpleChaseSolver(Group group, int maxDistance)
    {
        this.group = group;
        
        applicableFixtures = new ArrayList<>();

        applicableFixtures.addAll(
            group.GetFixtures().stream()
                .filter(Solver::GetIsApplicableToFixture)
                .collect(Collectors.toList()
            )
        );
        this.maxDistance = maxDistance;
    }

    @Override
    public void SolveInternal()
    {
        if(value != 0.0f)
        {
            for(int currentFixtureNumber = 0; currentFixtureNumber < applicableFixtures.size(); currentFixtureNumber++)
            {
                float chasePosition = (float)(currentFixtureNumber) / (float)(applicableFixtures.size() - 1);
                float distance1 = Math.abs(chasePosition - value);
                float distance;
                if(wrapping)
                {
                    float distance2 = Math.abs((chasePosition - 1) - value);
                    distance = (distance1 < distance2) ? distance1 : distance2;
                }
                else
                {
                    distance = distance1;
                }
                float percentage = 1 - distance;
                float minPercentage = 1 - ((float)maxDistance / (float)applicableFixtures.size());
                float percentageRange = 1 - minPercentage; 
                float adjustedPercentage = ((percentage) / percentageRange) - (minPercentage / percentageRange);
                if(adjustedPercentage < 0)
                {
                    adjustedPercentage = 0;
                }
                else if(adjustedPercentage > 1)
                {
                    adjustedPercentage = 1;
                }
                
                for(Channel channel : applicableFixtures.get(currentFixtureNumber).GetChannels())
                {
                    if(Solver.SolvesForChannel(channel.GetName(), solvableChannels))
                    {
                        float newValue = channel.GetValue() * adjustedPercentage;
                        channel.SetValue(newValue);
                    }
                }
            }
        }
    }

    @Override
    public String GetType()
    {
        return "simpleChase";
    }

    @Override
    public void Set(String attributeName, float value)
    {
        switch(attributeName)
        {
            case "Chase":
                this.value = value;
                break;
        }

    }

    @Override
    public boolean SolvesAttribute(String attribute)
    {
        return SolvesAttribute(attribute, solvableAttributes);
    }
    
    public static boolean GetIsApplicableToFixture(Fixture fixture)
    {
        return Solver.GetIsApplicableToFixture(fixture, solvableChannels);
    }

    @Override
    public Collection<? extends Attribute> GetAttributes()
    {
        ArrayList<Attribute> attributes = new ArrayList<>();
        attributes.add(new Attribute("maxDistance", maxDistance + ""));
        return attributes;
    }
    
    @Override
    public float GetAttribute(String name)
    {
        return value;
    }

    public int GetMaxDistance()
    {
        return maxDistance;
    }

    public void SetMaxDistance(int maxDistance)
    {
        this.maxDistance = maxDistance;
    }

}
